package main

import (
	"compress/gzip"
	"encoding/json"
	"errors"
	"fmt"
	"huanxinchat/conf"
	"huanxinchat/models"
	"huanxinchat/utils"
	"strconv"
	"time"

	"io/ioutil"
	"strings"

	"github.com/astaxie/beego/logs"
	"github.com/robfig/cron"
)

var token string

type Content struct {
	Msg     string `json:"msg"`
	MsgType string `json:"type"`
	MsgUrl  string `json:"url"`
}

type Payload struct {
	From   string    `json:"from"`
	To     string    `json:"to"`
	Bodies []Content `json:"bodies"`
}

type Record struct {
	Timestamp string  `json:"timestamp"`
	Payload   Payload `json:"payload"`
}

//定时同步聊天记录
func loadChatCron() {
	c := cron.New()
	c.AddFunc("0 0 * * * *", func() {
		downloadChat("")
	})
	c.Start()
}

//定时同步出错的聊天记录
func loadChatErrCron() {
	c := cron.New()
	c.AddFunc("0 30 * * * *", func() {
		cerr, err := models.ChatErrorRead()
		if err == nil {
			err = downloadChat(cerr.Starttime)
			if err == nil {
				models.ChatErrorUpdate(cerr.Id, 1)
			} else {
				models.ChatErrorUpdate(cerr.Id, 0)
			}

		}
	})
	c.Start()
}

//初始化token
func init() {
	var err error
	token, err = getToken()
	if err != nil {
		logs.Error(err)
		return
	}
}

func downloadChat(syncTime string) error {
	if syncTime == "" {
		syncTime = time.Now().Add(-time.Hour * 2).Format("2006010215")

	}

	//获取下载地址
	urls, err := getChatLoadUrl(syncTime)
	if err != nil {
		insertErr(err.Error(), 400, syncTime)
		return err
	}

	headMap := make(map[string]string)
	for _, url := range urls {
		//下载聊天记录文件
		resp, err := utils.DoResp("GET", url, headMap, "")
		if err != nil {
			//如果获取失败则重新同步
			insertErr(err.Error(), 400, syncTime)
			logs.Error(err)
			return err
		}
		defer resp.Body.Close()

		//解压记录文件
		gzip, err := gzip.NewReader(resp.Body)
		if err != nil {
			logs.Error(err)
			return nil
		}
		defer gzip.Close()

		//读取记录文件
		result, err := ioutil.ReadAll(gzip)
		if err != nil {
			logs.Error(err)
			return nil
		}

		//解析记录文件
		parseChatInfo(result)

	}

	return nil
}

func getChatLoadUrl(syncTime string) (urls []string, chatErr error) {

	headMap := make(map[string]string)
	headMap["Content-Type"] = "application/json"
	headMap["Authorization"] = "Bearer " + token

	urlStr := fmt.Sprintf("https://a1.easemob.com/%s/%s/chatmessages/%s", conf.ORG_NAME, conf.APP_NAME, syncTime)

	result, code, err := utils.DoBytes("GET", urlStr, headMap, "")
	if err != nil {
		logs.Error(err)
		//获取数据出错，插入错误记录表重新同步
		chatErr = err
		return
	}

	//401 is token invalid
	if code == 401 {
		token, err = getToken()
		if err != nil {
			logs.Error(err)
			//如果再次获取token还是失败的则插入错误表，等待重新同步
			chatErr = err
			return
		}

		headMap["Authorization"] = "Bearer " + token
		result, code, err = utils.DoBytes("GET", urlStr, headMap, "")
		if err != nil {
			logs.Error(err)
			//重新同步
			chatErr = err
			return
		}

	}
	if code == 200 {
		//错误不需要插入错误表，同步第一次解析出错，再同步一次结果也是 一样的
		msg, err := parseJson(result)
		if err != nil {
			logs.Error(err)
			return
		}
		if data, ok := msg["data"]; ok {
			items := data.([]interface{})
			urls = make([]string, 0, len(items))
			for _, item := range items {
				urlObj := item.(map[string]interface{})
				urls = append(urls, urlObj["url"].(string))
			}
		}

	} else if code == 400 {
		//400（要取得历史记录已过期、要取得的历史记录文件还没有生成）
		//只有400才有插入错误表，重新同步的必要
		logs.Error(string(result))
		chatErr = errors.New(string(result))
		return

	} else {
		if err != nil {
			logs.Error(string(result))
		}
	}

	return
}

//将错误信息插入错误表
func insertErr(msg string, code int, starttime string) {
	cer := &models.ChatError{Msg: msg, Code: code, Starttime: starttime}
	err := models.ChatErrorAdd(cer)
	if err != nil {
		logs.Error(err)
	}
}

//get token
func getToken() (token string, err error) {

	headMap := make(map[string]string)
	headMap["Content-Type"] = "application/json"

	urlStr := fmt.Sprintf("https://a1.easemob.com/%s/%s/token", conf.ORG_NAME, conf.APP_NAME)

	result, code, err := utils.DoBytes("POST", urlStr, headMap, conf.GENERATOR_TOKEN_KEY)
	if code == 200 {
		res, err := parseJson(result)
		if err == nil {
			token = res["access_token"].(string)
		} else {
			logs.Error(err)
		}

	}

	return
}

//解析记录文件
func parseChatInfo(datas []byte) (err error) {

	lines := strings.Split(strings.TrimSpace(string(datas)), "\n")
	var record *Record = &Record{}
	chatRecord := &models.ChatRecord{}
	for _, line := range lines {
		err = json.Unmarshal([]byte(line), record)
		if err != nil {
			logs.Error(err)
			continue
		}
		timeInt, err := strconv.ParseInt(record.Timestamp, 10, 64)
		if err != nil {
			logs.Error(err)
			continue
		}

		chatRecord.Chattime = time.Unix(timeInt/1000, 0)

		chatRecord.ContentType = record.Payload.Bodies[0].MsgType
		if chatRecord.ContentType == "txt" {
			chatRecord.Content = record.Payload.Bodies[0].Msg

		} else if chatRecord.ContentType == "img" {
			chatRecord.Content = record.Payload.Bodies[0].MsgUrl

		}
		chatRecord.From = record.Payload.From
		chatRecord.To = record.Payload.To
		chatRecord.Id = 0

		_, err = models.AddChatRecode(chatRecord)
		if err != nil {
			logs.Error(err)
			continue
		}

	}

	return
}

//parse json
func parseJson(data []byte) (res map[string]interface{}, err error) {
	res = make(map[string]interface{})
	err = json.Unmarshal(data, &res)

	return
}
